package org.openstreetmap.josm.plugins.cadastre_es;

import java.awt.image.BufferedImage;

@SuppressWarnings("unchecked")
public class Pixel implements Comparable {
	static final int BUILDING = 0xFFFFEBEB;
	static final int BUILDING2 = 0xFFFFE3E3;
	static final int BORDER = 0xFFFF0000;
	static final int FOLLOWED = 0xFF00FF00;
	static final int FBUILDING = 0xFFAAFFAA;
	static final int WHITE = 0xFFFFFFFF;
	static final int VERTEX = 0xFF0000FF;
	static final int BOTTOM_MARGIN = 12;
	
	static final int BUILDING_RED = 255;
	static final int BUILDING_GREEN = 235;
	static final int BUILDING_BLUE = 235;
	static final int COLOR_MARGIN = 30;
	static BufferedImage img;    
	
	private int x;
	private int y;
	private Boolean outside;
	
	public Pixel() {
		this.x = 1;
		this.y = 1;
	}
	
	public Pixel(int x, int y) {
		this.x = x;
		this.y = y;
		outside = outside();
	}
	
	public Pixel(Pixel p) {
		if (p != null) {
			this.x = p.x;
			this.y = p.y;
			outside = outside();
		}
		else {
			this.x = -1;
			this.y = -1;
			outside = true;
		}
			
	}
	
	public void copy(Pixel p) {
		this.x = p.x;
		this.y = p.y;
		outside = outside();
	}
	
	public int x() {
		return x;
	}
	
	public int y() {
		return y;
	}
	
	public void x(int val) {
		x = val;
		outside = outside();
	}
	
	public void y(int val) {
		y = val;
		outside = outside();
	}
	
	public Pixel top() {
		return new Pixel(x, y-1);
	}
	
	public Pixel topRight() {
		return new Pixel(x+1, y-1);
	}

	public Pixel right() {
		return new Pixel(x+1, y);
	}

	public Pixel bottomRight() {
		return new Pixel(x+1, y+1);
	}

	public Pixel bottom() {
		return new Pixel(x, y+1);
	}

	public Pixel bottomLeft() {
		return new Pixel(x-1, y+1);
	}

	public Pixel left() {
		return new Pixel(x-1, y);
	}
	
	public Pixel topLeft() {
		return new Pixel(x-1, y-1);
	}
	
	public int getRGB() {
		if (!outside)
			return img.getRGB(x, y);
		else 
			return WHITE;
	}
	
	public void setRGB(int color) {
		//if (x == 20 && y == 91)
		//	System.out.println("debug");
		if (!outside)
			img.setRGB(x, y, color);
	}
	
	public Boolean isWhite() {
		return !outside && (this.getRGB() == WHITE || (this.getRGB() != FOLLOWED && this.getRGB() != VERTEX && this.getRGB() != BUILDING));
	}
	
	public Boolean isWhiteNonFBuilding() {
		return !outside && (this.getRGB() == WHITE || (this.getRGB() != BORDER && this.getRGB() != FOLLOWED && this.getRGB() != VERTEX && this.getRGB() != BUILDING && this.getRGB() != FBUILDING));
	}
	
	public Boolean isBorder() {
		return !outside && (this.getRGB() == BORDER || this.getRGB() == FOLLOWED || this.getRGB() == VERTEX);
	}
	
	public Boolean isHole() {
		return !outside && (this.getRGB() == WHITE || (this.getRGB() != FOLLOWED && this.getRGB() != VERTEX && this.getRGB() != BUILDING && this.getRGB() != FBUILDING));
	}
	
	public Boolean isNewBorder() {
		return !outside && (this.getRGB() == BORDER);
	}
	
	public Boolean isVertex() {
		return !outside && (this.getRGB() == VERTEX);
	}
	
	public Boolean isFollowed() {
		return !outside && (this.getRGB() == FOLLOWED || this.getRGB() == VERTEX);
	}
	
	public Boolean isFBuilding() {
		return !outside && (this.getRGB() == FBUILDING);
	}
	
	public Boolean isBuilding() {
	    //Color color = new Color(this.getRGB());
		//return !outside && Math.abs(color.getRed() - BUILDING_RED) < COLOR_MARGIN && Math.abs(color.getGreen() - BUILDING_GREEN) < COLOR_MARGIN && Math.abs(color.getBlue() - BUILDING_BLUE) < COLOR_MARGIN;
	    return !outside && (this.getRGB() == BUILDING || this.getRGB() == BUILDING2);
	}
	
	public Boolean isNotBorder() {
		return !outside && (!((this.getRGB() == BORDER) || (this.getRGB() == FOLLOWED) || (this.getRGB() == VERTEX)));
	}
	
	public Boolean outside() {
		return (x < 0 || y < 0 || (x >= (img.getWidth())) || (y >= (img.getHeight() - BOTTOM_MARGIN)));
	}
	
	public static Boolean sOutside(int x, int y) {
		return (x < 0 || y < 0 || (x >= (img.getWidth())) || (y >= (img.getHeight() - BOTTOM_MARGIN)));
	}
	
	public boolean equal(Pixel p) {
		if (p == null)
			return false;
		return ((x == p.x) && (y == p.y));		
	}
	
	public Pixel duplicate() {
		return new Pixel(x, y);
	}
	
	public int distance(Pixel p) {
		return Math.abs(this.x - p.x) + Math.abs(this.y - p.y);
	}
	
	public int betterDistance(Pixel p) {
		return (int) Math.sqrt((this.x - p.x) * (this.x - p.x) + (this.y - p.y) * (this.y - p.y));
	}
	
	public enum direction { UNDEFINED, TOP, TOPLEFT, TOPRIGHT, BOTTOM, BOTTOMLEFT, BOTTOMRIGHT, LEFT, RIGHT };
	
	public direction getDirection(Pixel p) {
		if (((p.x - x) == 0) &&  ((p.y - y) == -1))
			return direction.TOP;
		else if (((p.x - x) == 1) &&  ((p.y - y) == -1))
			return direction.TOPRIGHT;
		else if (((p.x - x) == 1) &&  ((p.y - y) == 0))
			return direction.RIGHT;
		else if (((p.x - x) == 1) &&  ((p.y - y) == 1))
			return direction.BOTTOMRIGHT;
		else if (((p.x - x) == 0) &&  ((p.y - y) == 1))
			return direction.BOTTOM;
		else if (((p.x - x) == -1) &&  ((p.y - y) == 1))
			return direction.BOTTOMLEFT;
		else if (((p.x - x) == -1) &&  ((p.y - y) == 0))
			return direction.LEFT;
		else
			return direction.TOPLEFT;
	}
	
	public float angle(Pixel p) {
		int dx = p.x - x;
		int dy = p.y - y;
		double alpha;
		
		if (dx > 0) {
			if ((-dy) >= 0)
				alpha = Math.atan(((double)-dy)/((double)dx));
			else
				alpha = 2 * Math.PI + Math.atan(((double)-dy)/((double)dx));
		}
		else if (dx < 0) {
			alpha = Math.atan(((double)-dy)/((double)dx)) + Math.PI;
		}
		else {
			if ((-dy) > 0)
				alpha = Math.PI / 2;
			else if ((-dy) < 0)
				alpha = 3 * Math.PI / 2;
			else
				return 0;
		}
		return (float) alpha;
	}
	
	public Boolean inLimit() {
		return x == 0 || y == 0 || x == (img.getWidth() - 1) || y == (img.getHeight() - 1 - Pixel.BOTTOM_MARGIN);
	}
	
	public int hashCode() {
		return x*100000 + y;
	}
	
	public boolean equals(Object obj) {
		if (obj == null)
			return false;
		Pixel p = (Pixel)obj;
		return ((x == p.x()) && (y == p.y()));		
	}
	
	public int compareTo(Object p) {
        if (this.x == ((Pixel) p).x())
            return 0;
        else if ((this.x) > ((Pixel) p).x())
            return 1;
        else
            return -1;
    }
	
	public double getLat() {
		return Process.topLat - ((Process.topLat - Process.bottomLat) * ((double) (y + 1) / (double) Process.height));
	}
	
	public double getLon() {
		return Process.leftLon + ((Process.rightLon - Process.leftLon) * ((double) (x + 1) / (double) Process.width));
	}
}